Bus IO Programming Example

The following code fragment illustrates the Bus I/O calls in a PCI driver.

/*++

*

* Copyright (c) 1996-2008. IntervalZero, Inc. All rights reserved.

*

* Module Name:

* RtxLevelIntTest.c

*

* Abstract:

* Test code for processing level triggered interrupts

* using the PCI-DIO-96 card. Developed from the pci test

* and gmpt driver.

*

* Author:

*

* Environment:

*

* RTX RTSS application

*

* Revision History:

* Version 1 Jun 1 1998

--*/

#include <windows.h>

#include <stdio.h>

#include <rtapi.h>

//

// Get and Set Long value macros.

//

#define PTL(x) (*(PLONG)(x))

/* DO NOT CHANGE PARAMETERS BELOW THIS POINT */

#define BASE_ADDR _base_addr

/* PPI A */

#define PORTA_ADDR ((PUCHAR) BASE_ADDR + 0x00) // Port A (Port 0)

#define PORTB_ADDR ((PUCHAR) BASE_ADDR + 0x01) // Port B (Port 1)

#define PORTC_ADDR ((PUCHAR) BASE_ADDR + 0x02) // Port C (Port 2)

#define CNFG_ADDR ((PUCHAR) BASE_ADDR + 0x03) // Config Register

/* PPI B */

#define B_PORTA_ADDR ((PUCHAR) BASE_ADDR + 0x04) // PPI B Port A (Port 3)

#define B_PORTB_ADDR ((PUCHAR) BASE_ADDR + 0x05) // PPI B Port B (Port 4)

#define B_PORTC_ADDR ((PUCHAR) BASE_ADDR + 0x06) // PPI B Port C (Port 5)

#define B_CNFG_ADDR ((PUCHAR) BASE_ADDR + 0x07) // PPI B Config Port

/* PPI C */

#define C_PORTA_ADDR ((PUCHAR) BASE_ADDR + 0x08) // PPI C Port A (Port 6)

#define C_PORTB_ADDR ((PUCHAR) BASE_ADDR + 0x09) // PPI C Port B (Port 7)

#define C_PORTC_ADDR ((PUCHAR) BASE_ADDR + 0x0A) // PPI C Port C (Port 8)

#define C_CNFG_ADDR ((PUCHAR) BASE_ADDR + 0x0B) // PPI C Config Port

/* PPI D */

#define D_PORTA_ADDR ((PUCHAR) BASE_ADDR + 0x0C) // PPI D Port A (Port 9)

#define D_PORTB_ADDR ((PUCHAR) BASE_ADDR + 0x0D) // PPI D Port B (Port 10)

#define D_PORTC_ADDR ((PUCHAR) BASE_ADDR + 0x0E) // PPI D Port C (Port 11)

#define D_CNFG_ADDR ((PUCHAR) BASE_ADDR + 0x0F) // PPI D Config Port

/* Counter/Timer */

#define CLOCKA ((PUCHAR) BASE_ADDR + 0x10) // Clock or Counter 0

#define CLOCKB ((PUCHAR) BASE_ADDR + 0x11) // Clock or Counter 1

#define CLOCKC ((PUCHAR) BASE_ADDR + 0x12) // Clock or Counter 2

#define CLOCK_CTRL ((PUCHAR) BASE_ADDR + 0x13) // Clock or Counter Control

/* Interrupt control */

#define INTR_CTRL1 ((PUCHAR) BASE_ADDR + 0x14) // First interrupt control reg

#define INTR_CTRL2 ((PUCHAR) BASE_ADDR + 0x15) // Second interrupt control reg

#define INTR_CLEAR ((PUCHAR) BASE_ADDR + 0x16) // Interrupt Clear Register

//#define CNFG_VAL 0x80 // 1000 0000 - Port A Output Port B Output Port C Output

//#define CNFG_VAL 0x90 // p 6-10 - Port A Input Port B Output Port C Output

#define CNFG_VAL 0x15 // p 6-10 - Port A Input Port B Input Port C Input

// Note: Standard RTX time units are 100 ns long, or 10 units per microsecond.

#define STOP_TIME 5 // Minutes for shutdown handler to wait

// after stopping before terminating.

// 0 for indefinite.

#define BASE_ADDR _base_addr

/* respond: the interrupt handler */

void _stdcall respond(PVOID addr);

/* fail: utility routine for fatal errors. */

void fail(char *mesg);

/* shutdownh: the shutdown handler */

void _stdcall shutdownh(PVOID dummy, long cause);

PCHAR _base_addr;

PCI_SLOT_NUMBER SlotNumber;

PPCI_COMMON_CONFIG PciData;

UCHAR buffer[PCI_COMMON_HDR_LENGTH];

ULONG IntCountDown; // number of ints calls wait for clearing

ULONG IntCountTotal; // total int count

ULONG IntCountCLear; // total int cleared

BOOLEAN IntDone; // when we are done

void

main( int argc, PCHAR *argv )

{

ULONG i; // logical slot number for the PCI adapter

ULONG f; // function number on the specified adapter

ULONG bytesWritten; // return value from RtGetBusDataByOffset

ULONG bus; // bus number

BOOLEAN flag; 
LARGE_INTEGER BAR0; // base port address of the MITE
LARGE_INTEGER BAR1; // base port address of the board registers
LARGE_INTEGER tBAR0, tBAR1; // translated base addresses (system
// mapped addresses) returned by RtMapMemory
ULONG AddressSpace = 0; // indicates memory space
ULONG window_data_value = 0; // not really needed...
PCHAR vBAR0=NULL, vBAR1 = NULL; // pointer virtual memory addresses
// returned by RtMapMemory
ULONG IrqLevel; // interrupt level
ULONG IrqVectr; // interrupt vector
PVOID ihr; // interrupt handler
ULONG intbusnumb; // Interrupt bus number
BAR0.QuadPart = 0;
BAR1.QuadPart = 0;
tBAR0.QuadPart = 0;
tBAR1.QuadPart = 0;
PciData = (PPCI_COMMON_CONFIG) buffer; 
SlotNumber.u.bits.Reserved = 0; 
flag = TRUE;
//
// Search for the PCI-DIO-96 card
//
for (bus = 0; flag; bus++) 
{ 
for (i = 0; i < PCI_MAX_DEVICES && flag; i++)
{ 
SlotNumber.u.bits.DeviceNumber = i; 
for (f = 0; f < PCI_MAX_FUNCTION; f++)
{ 
SlotNumber.u.bits.FunctionNumber = f; 
bytesWritten = RtGetBusDataByOffset (
PCIConfiguration, 
bus, 
SlotNumber.u.AsULONG, 
PciData, 
0,
PCI_COMMON_HDR_LENGTH 
); 
if (bytesWritten == 0) 
{ 
// out of PCI buses 
flag = FALSE; 
break; 
} 
if (PciData->VendorID == PCI_INVALID_VENDORID)
{ 
// no device at this slot number, skip to next slot
break; 
} 
//
// A device is found, if this is our card, 
 then 
// print out all the PCI configuration information 
 
// and set the variables.
//
if ((PciData->VendorID == 0x1093) && 
 (PciData->DeviceID == 0x0160))
{
// Set IRQ values for attaching interrupt below
IrqLevel = PciData->u.type0.InterruptLine; // interrupt level
IrqVectr = IrqLevel; // interrupt IRQ
// Put the BusAddresses into other variables
BAR0.QuadPart = PciData->u.type0.BaseAddresses[0]; 
// MITE address 
BAR1.QuadPart = PciData->u.type0.BaseAddresses[1];
// new board address
intbusnumb = bus;
printf("\nPCI-DIO-96:\n");
printf("------------------------------------------\n");
printf("BusNumber:\t\t%d\n", bus);
printf("DeviceNumber:\t\t%d\n", i); 
printf("FunctionNumber:\t\t%d\n", f); 
printf("VendorID:\t\t0x%x\n", PciData->VendorID);
printf("DeviceID:\t\t0x%x\n", PciData->DeviceID);
printf("Command:\t\t0x%x\n", PciData->Command);
printf("Status:\t\t\t0x%x\n", PciData->Status);
printf("RevisionID:\t\t0x%x\n", PciData->RevisionID);
printf("ProgIf:\t\t\t0x%x\n", PciData->ProgIf);
printf("SubClass:\t\t0x%x\n", PciData->SubClass);
printf("BaseClass:\t\t0x%x\n", PciData->BaseClass);
printf("CacheLineSize:\t\t0x%x\n", PciData->CacheLineSize); 
printf("LatencyTimer:\t\t0x%x\n", PciData->LatencyTimer);
printf("HeaderType:\t\t0x%x\n",PciData->HeaderType);
printf("BIST:\t\t\t0x%x\n", PciData->BIST);
printf("BaseAddresses[0]:\t0x%08x\n",PciData->u.type0.BaseAddresses[0]);
printf("BaseAddresses[1]:\t0x%08x\n",PciData->u.type0.BaseAddresses[1]);
printf("BaseAddresses[2]:\t0x%08x\n",PciData->u.type0.BaseAddresses[2]);
printf("BaseAddresses[3]:\t0x%08x\n",PciData->u.type0.BaseAddresses[3]);
printf("BaseAddresses[4]:\t0x%08x\n",PciData->u.type0.BaseAddresses[4]);
printf("BaseAddresses[5]:\t0x%08x\n",PciData->u.type0.BaseAddresses[5]);
printf("ROMBaseAddress:\t\t0x%08x\n",PciData->u.type0.ROMBaseAddress);
printf("InterruptLine:\t\t%d\n",PciData->u.type0.InterruptLine);
printf("InterruptPin:\t\t%d\n",PciData->u.type0.InterruptPin);
printf("MinimumGrant:\t\t%d\n",PciData->u.type0.MinimumGrant);
printf("MaximumLatency:\t\t%d\n",PciData->u.type0.MaximumLatency);
printf("\n");
printf(" BAR0: BusRelativeAddress: 0x%08x\n", BAR0.LowPart); 
printf(" BAR1: BusRelativeAddress: 0x%08x\n", BAR1.LowPart); 
//
// Translate the base port addresses to system mapped addresses.
//
if (! RtTranslateBusAddress( 
PCIBus, 
0, 
BAR0,
&AddressSpace, 
&tBAR0
))
{
fail ("tBAR0: RtTranslateBusAddress failed");
}
else
{
printf("tBAR0: SystemMappedAddress: 0x%08x\n", tBAR0.LowPart); 
} 
if (! RtTranslateBusAddress(PCIBus, 
0, 
BAR1,
&AddressSpace, 
&tBAR1
))
{ 
fail ("tBAR1: RtTranslateBusAddress failed.");
}
else
{
printf("tBAR1: SystemMappedAddress: 0x%08x\n", tBAR1.LowPart); 
} 
//
// Map the addresses to virtual addresses 
 the software can use
//
vBAR0=RtMapMemory( tBAR0, 4*1024, MmNonCached); 
 // 4K, cache disabled
if (vBAR0 == 0)
{ 
printf(" BAR0: Failure on RtMapMemory\nError=%d\n", 
 GetLastError());
}
else
{ 
printf(" BAR0: VirtualMemoryAddress: 0x%08x\n",vBAR0);
}
vBAR1=RtMapMemory( tBAR1, 4*1024, 0); // 
 4K, cache disabled
if (!vBAR1)
{ 
printf(" BAR1: Failure on RtMapMemory\nError=%d\n", 
 GetLastError());
}
else
{ 
printf(" BAR1: VirtualMemoryAddress: 0x%08x\n", vBAR1);
}
//
// Set the command parameter so we can access 
 the PCI device's
// control registers.
//
PciData->Command = (PCI_ENABLE_IO_SPACE 
 |
PCI_ENABLE_MEMORY_SPACE |
PCI_ENABLE_BUS_MASTER |
PCI_ENABLE_WRITE_AND_INVALIDATE);
RtSetBusDataByOffset(PCIConfiguration,
bus,
SlotNumber.u.AsULONG,
PciData,
0,
PCI_COMMON_HDR_LENGTH);
//
// Initialize the card
// No 1 MB limit - no need to remap the memory.
// Note: You need to use the BusRelativeAddress 
 to set the 
// window_data_value. 
//
window_data_value = ( (0xffffff00 &
(ULONG)BAR1.LowPart) | (0x00000080) );
PTL(vBAR0+0x000000c0) = window_data_value;
}// dio 96
} // max_function
} // max_devices
} // flag
//
// At this point the card has been found, 
 memory mapped, so we go ahead
// and attempt to utilize the card for testing
//
_base_addr=vBAR1; // Memory already mapped
// Disable interrupts from I/O ports
*(INTR_CTRL1) = 0x00;
// Disable interrupts from counters
*(INTR_CTRL2) = 0x00;
//
// Attempt to attach to this interrupt, using 
 data acquired above
//
ihr = RtAttachInterruptVector(NULL, // security 
 attributes (default)
0, // stacksize (default)
respond, // interrupt handler
NULL, // context argument
RT_PRIORITY_MAX,
PCIBus, // interface type (PCI)
intbusnumb, // bus number
IrqLevel, // interrupt level
IrqVectr); // interrupt vector
}

See Also

Bus IO Overview